package ru.amse.jsynchro.kernel;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;

import ru.amse.jsynchro.fileSystem.FileSystem;
import ru.amse.jsynchro.logging.ILogger;
import ru.amse.jsynchro.logging.LogFactory;

@SuppressWarnings("serial")
public class NodeData implements Serializable {
    protected String myPath;
    protected String myName;
    protected long myLength;
    protected boolean isDirectory;
    protected String myValue;
    protected static DigestAlgorithm digest = new DigestAlgorithm(); 
    protected final IDirectoryTree myTree;
    private boolean compareFileContent = true;
    private transient ILogger logger = LogFactory.getLogger();
    private boolean canExecute;
    private boolean canRead;
    private boolean canWrite;
    private boolean isHidden;
    public NodeData(File file, IDirectoryTree tree, boolean compare) {
        myTree = tree;
        myPath = file.getAbsolutePath();
//        myName = file.getName().toLowerCase();
        myName = file.getName();
        myLength = file.length();
        isDirectory = file.isDirectory();
        if (!isDirectory) {
            myValue = digest.getValue(file);
        }
        canExecute = file.canExecute();
        canExecute = file.canExecute();
        canExecute = file.canExecute();
        isHidden = file.isHidden();
        
    }
    
    public NodeData(String name, long length, boolean isDirectory, IDirectoryTree tree, boolean compare) {
        myTree = tree;
        myName = name;
        if ((length != 0) && (isDirectory)) {
            throw new IllegalArgumentException("directory length must be 0");
        }
        myLength = length;
        this.isDirectory = isDirectory;
        myValue = "1";
    }
    
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof NodeData)) {
            return false;
        }
        NodeData node = (NodeData) obj;
        if ((isDirectory == node.isDirectory) && (node.isDirectory == true) && ((myName.equals(node.myName)))) {
            return true;
        }
        if ((isDirectory == node.isDirectory) && (isDirectory == false) && (myLength == node.myLength) 
                && (myName.equals(node.myName)) && (myValue.equals(node.myValue))) {
            if (compareFileContent) {
                return byteCompare(node);
            } else {
                return true;
            }
        }
        return false;
    }
    
    /**
     * sets flag to byte compare file in equals() method.
     * Usually file content is not compared with remote file
     * @param setCompareFileContent if true 
     */
    public void setSetCompareFileContent(boolean setCompareFileContent) {
        this.compareFileContent = setCompareFileContent;
    }

    private boolean byteCompare(NodeData node) {
        InputStream fis1 = null;
        InputStream fis2 = null;
        FileSystem fs1 = null;
        FileSystem fs2 = null;
        try {
            fs1 = myTree.getFS();
            fs2 = node.myTree.getFS();
            long length1 = fs1.getFileLength(myPath);
            long length2 = fs2.getFileLength(node.myPath);
            
            if (length1 != length2) {
                logger.info("not equal " + myTree.getPath());
                return false;
            }
            fis1 = fs1.openInputStream(myPath, length1);
            fis2 = fs2.openInputStream(node.myPath, length2);
            int c = 0;
            while ((c = fis1.read()) >= 0) {
                if (c != fis2.read()) {
                    return false;
                }
            }
            
//            int readLength = 1024;
//            byte[] buf1 = new byte[readLength];
//            byte[] buf2 = new byte[readLength];
//            int bytesRead1 = 0;
////            int bytesRead2 = 0;
//            while ((length1 > 0) && (length2 > 0)) {
//                bytesRead1 = fis1.read(buf1);
//                if (bytesRead1 != fis2.read(buf2)) {
//                    throw new AssertionError("error in reading");
//                }
//                if (bytesRead1 == readLength) {
//                    Arrays.equals(buf1, buf2);
//                } else {
//                    return areArraysEqual(buf1, buf2, bytesRead1);
//                }
//                length1 -= readLength;
//                length2 -= readLength;
            fs1.closeInputStream(fis1);
            fs2.closeInputStream(fis2);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e2) {
            e2.printStackTrace();
        } finally {
            try {
                fs1.closeInputStream(fis1);
                fs2.closeInputStream(fis2);
            } catch (IOException e) {
                logger.exception(e);
            }
            
        }
        return true;
        
    }
    
//    private boolean areArraysEqual(byte[] arr1, byte[] arr2, int bytesRead) {
//        for (int i = 0; i < bytesRead; i++) {
//            if (arr1[i] != arr2[i]) {
//                System.out.println("in areArrr");
//                return false;
//            }
//        }
//        return true;
//    }
    
//    private boolean bitCompare(NodeData node, FileSystemStrategy fsStr) {
//        try {
//            InputStream fis1 = getFileInputStream(myPath);
//            InputStream fis2 = new FileInputStream(node.myPath);
//            byte[] buf1 = new byte[1024];
//            byte[] buf2 = new byte[1024];
//            int num1;
//            int num2;
//            while (((num1 = fis1.read(buf1)) != -1) && ((num2 = fis2.read(buf2)) != -1)) {
//                if (num1 != num2) {
//                    return false;
//                }
//                if (!Arrays.equals(buf1, buf2)) {
//                    return false;
//                }
//            }
//            fis1.close();
//            fis2.close();
//        } catch (FileNotFoundException e) {
//            e.printStackTrace();
//        } catch (IOException e2) {
//            e2.printStackTrace();
//        }
//        return true;
//        
//    }
    
    public String getName() {
        return myName;
    }
    
    public boolean isDirectory() {
        return isDirectory;
    }
    
    public String getPath() {
        return myPath;
    }

    public byte compareAttrs(NodeData myData) {
        byte attrs = 0;
        if (canRead != myData.canRead) {
            attrs |= 0x01;
        }
        if (canWrite != myData.canWrite) {
            attrs |= 0x02;
        }
        if (canExecute != myData.canExecute) {
            attrs |= 0x04;
        }
        if (isHidden != myData.isHidden) {
            attrs |= 0x08;
        }
        return attrs;
    }
}